using System.Threading.RateLimiting;
using System.Globalization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.RateLimiting;
using Microsoft.IdentityModel.Tokens;
using Digitalmes.WebApi.Infrastructure.Jwt;

namespace Digitalmes.WebApi.Extensions;

public static class DependencyInjections
{
    /// <summary>
    /// 配置 WebApi 服务。
    /// </summary>
    /// <param name="builder"></param>
    public static void AddWebApiSetup(this IHostBuilder builder)
    {
        builder.ConfigureServices((hostBuilder, services) =>
        {
            services.AddScoped<ICurrentUser, CurrentUser>();
            services.AddKeyedTransient<ICurrentUser, CurrentUser>(typeof(ICurrentUser)); // 另注册Transient模式，供内部单例实体调用。
            services.AddHttpContextAccessor();

            // Customise default API behaviour
            services.Configure<ApiBehaviorOptions>(options =>
                options.SuppressModelStateInvalidFilter = true);

            // 注册 CORS
            services.AddCorsSetup(hostBuilder.Configuration);

            // 注册 JWT 配置
            services.AddAuthenticationSetup(hostBuilder.Configuration);

            // 注册限流策略
            services.AddRateLimiterSetup(hostBuilder.Configuration);

            // 注册全局异常（可注册多个，会按顺序调用）
            services.AddExceptionHandler<GlobalExceptionHandler>();
            services.AddProblemDetails();

            // IResult 的 JSON 配置
            services.ConfigureHttpJsonOptions(options =>
            {
                options.SerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; // 忽略循环引用
                options.SerializerOptions.WriteIndented = true;
                options.SerializerOptions.Converters.Add(new DateTimeJsonConverter());
                options.SerializerOptions.Converters.Add(new DateTimeOffsetJsonConverter());
            });

            // 使用 OpenApi 文档
            services.AddOpenApi();

            // 注册 SignalR
            services.AddSignalR();

            services.AddEndpointsApiExplorer();
        });
    }

    /// <summary>
    /// 注册 CORS 配置
    /// </summary>
    /// <param name="services"></param>
    /// <param name="configuration"></param>
    private static void AddCorsSetup(this IServiceCollection services, IConfiguration configuration)
    {
        // 配置 CORS
        var origins = configuration["CorsOrigins"]?
            .Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(o => o.TrimEnd('/'))
            .ToArray() ?? [];
        services.AddCors(options => options.AddPolicy(name: CorsPolicy.DefaultPolicyName,
                policy =>
                {
                    policy.WithOrigins(origins)
                        .AllowAnyHeader()
                        .AllowAnyMethod()
                        .AllowCredentials()
                        .WithExposedHeaders("X-Refresh-Token");
                }));
    }

    /// <summary>
    /// 注册 JWT 认证配置
    /// </summary>
    /// <param name="services"></param>
    private static void AddAuthenticationSetup(this IServiceCollection services, IConfiguration configuration)
    {
        services.Configure<JwtTokenConfig>(configuration.GetSection("JwtSetting"));
        services.AddSingleton<IJwtAuthManager, JwtAuthManager>();

        var jwtConfig = configuration.GetSection("JwtSetting").Get<JwtTokenConfig>();

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(options =>
        {
            options.RequireHttpsMetadata = false;
            options.SaveToken = true;
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtConfig?.SecretKey ?? "")),

                ValidateIssuer = true,
                ValidIssuer = jwtConfig?.Issuer,

                ValidateAudience = true,
                ValidAudience = jwtConfig?.Audience,

                ValidateLifetime = true, // 验证 Token 有效时间

                ClockSkew = TimeSpan.FromMinutes(2), // 允许容忍的时间差
            };

            // Sending the access token in the query string is required when using WebSockets or ServerSentEvents
            // due to a limitation in Browser APIs. We restrict it to only calls to the SignalR hub in this code.
            options.Events = new JwtBearerEvents()
            {
                OnMessageReceived = context =>
                {
                    var accessToken = context.Request.Headers["access_token"];
                    var path = context.HttpContext.Request.Path;
                    // 若请求是来源于 SignalR Hub
                    if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/signalr-hubs/"))
                    {
                        context.Token = accessToken;
                    }
                    return Task.CompletedTask;
                },
            };
        });

        // 此处可定义授权策略，然后在 Endpoint 中可指定要应用的策略名称；或是直接在 Endpoint 设定授权策略。
        services.AddAuthorization(options =>
        {
            options.AddPolicy("admin", policy => policy.RequireRole("admin"));
        });
    }

    /// <summary>
    /// 注册限流策略。在 Endpoint 中使用 RequireRateLimiting("") 来应用指定的限流策略，
    /// 使用 EnableRateLimitingAttribute 或 DisableRateLimitingAttribute 来启用或禁用限流。
    /// </summary>
    /// <param name="services"></param>
    private static void AddRateLimiterSetup(this IServiceCollection services, IConfiguration _)
    {
        services.AddRateLimiter(limiterOptions =>
        {
            limiterOptions.OnRejected = (context, cancellationToken) =>
            {
                if (context.Lease.TryGetMetadata(MetadataName.RetryAfter, out var retryAfter))
                {
                    context.HttpContext.Response.Headers.RetryAfter =
                        ((int)retryAfter.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo);
                }

                context.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests;

                return new ValueTask();
            };

            limiterOptions.AddTokenBucketLimiter("token", options =>
            {
                options.TokenLimit = 100; // 令牌桶容量 多余会被丢弃
                options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst;
                options.QueueLimit = 2;
                options.ReplenishmentPeriod = TimeSpan.FromSeconds(30); // 间隔多长时间放一次令牌
                options.TokensPerPeriod = 60; // 每次向令牌桶放入的令牌数量
                options.AutoReplenishment = true;
            });
        });
    }

    /// <summary>
    /// 构建可复原的 HttpClient。
    /// </summary>
    /// <param name="services"></param>
    /// <param name="_"></param>
    //private static void AddResilienceHttpClientSetup(this IServiceCollection services, IConfiguration _)
    //{
    //    services.AddHttpClient("", client =>
    //    {
    //        client.BaseAddress = new Uri("");
    //    }).AddStandardResilienceHandler(options =>
    //    {
    //        options.Retry.MaxRetryAttempts = 3;
    //        options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(30); // 总的超时时间
    //        options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(5); // 每次重试的超时时间
    //        options.CircuitBreaker.BreakDuration = TimeSpan.FromSeconds(30); // 熔断时间
    //    });
    //}
}
